#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>


static dev_t dev_num;//定义dev_t类型(32位大小)的变量dev_num
static int major;  //定义int类型的主设备号major
static int minor;//定义int类型的 次设备号minor
static struct cdev cdev_test;  //定义struct cdev 类型结构体变量cdev_test，表示要注册的字符设备
struct class *class_test;//定于struct class *类型结构体变量class_test，表示要创建的类
struct device *device;    //设备


/*打开设备函数*/
static int chrdev_open(struct inode *inode, struct file *file)
{
    printk("This is chrdev_open \n");
    return 0;
}


static ssize_t chrdev_read(struct file *file,char __user *buf, size_t size, loff_t *off)
{
      /*本章实验重点******/
    char kbuf[32] = "This is read from kernel";//定义内核空间数据
     // copy_to_user:内核空间向用户空间传数据
    if (copy_to_user(buf, kbuf, strlen(kbuf)) != 0)     {
        printk("copy_to_user error\r\n"); //打印copy_to_user函数执行失败
        return -1;
    }
    printk("This is cdev_test_read\r\n");
    return 0;
}

/*向设备写入数据函数*/
static ssize_t chrdev_write(struct file *file,const char __user *buf,size_t size,loff_t *off)
{
     /*本章实验重点******/
    char kbuf[32] = {0};   //定义写入缓存区kbuf
    if (copy_from_user(kbuf, buf, size) != 0) // copy_from_user:用户空间向内核空间传数据
    {
        printk("copy_from_user error\r\n");//打印copy_from_user函数执行失败
        return -1;
    }
    printk("This is cdev_test_write\r\n");
    printk("kbuf is %s\r\n", kbuf);

    return 0;
}
static int chrdev_release(struct inode *inode, struct file *file)
{
    return 0;
}

static struct file_operations cdev_test_ops = {
	.owner=THIS_MODULE,//将owner字段指向本模块，可以避免在模块的操作正在被使用时卸载该模块
    .open = chrdev_open,//将open字段指向chrdev_open(...)函数
    .read = chrdev_read,//将open字段指向chrdev_read(...)函数
    .write = chrdev_write,//将open字段指向chrdev_write(...)函数
    .release = chrdev_release,//将open字段指向chrdev_release(...)函数
};//定义file_operations结构体类型的变量cdev_test_ops




static int __init chrdev_fops_init(void)//驱动入口函数
{
    int ret;//定义int类型的变量ret，用来判断函数返回值

    ret=alloc_chrdev_region(&dev_num,0,1,"chardev_num"); //通过动态方式进行设备号注册
    
    if(ret < 0){
            printk("alloc_chrdev_region is error\n");
    }   
        printk("alloc_chrdev_region is ok\n");
        major=MAJOR(dev_num);//通过MAJOR()函数进行主设备号获取
        minor=MINOR(dev_num);//通过MINOR()函数进行次设备号获取
        printk("major is %d\n",major);
        printk("minor is %d\n",minor);
 
        ////使用cdev_init()函数初始化cdev_test结构体，并链接到cdev_test_ops结构体
       cdev_init(&cdev_test,&cdev_test_ops);
        cdev_test.owner = THIS_MODULE;//将owner字段指向本模块，可以避免在模块的操作正在被使用时卸载该模块 

      ret= cdev_add(&cdev_test,dev_num,1);
        
    if(ret < 0 ){
        printk("cdev_add is error\n");
    }
    printk("cdev_add is ok\n");
    class_test  = class_create(THIS_MODULE,"class_test");//使用class_create进行类的创建，类名称为class_test
    device_create(class_test,NULL,dev_num,NULL,"device_test");//使用device_create进行设备的创建，设备名称为device_test



    return 0;
}
static void __exit chrdev_fops_exit(void)//驱动出口函数
{
    cdev_del(&cdev_test);//使用cdev_del()函数进行字符设备的删除
    unregister_chrdev_region(dev_num,1);//释放字符驱动设备号 
    device_destroy(class_test,dev_num);//删除创建的设备
    class_destroy(class_test);//删除创建的类


    printk("module exit \n");

}
module_init(chrdev_fops_init);//注册入口函数
module_exit(chrdev_fops_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("wang fang chen "); //作者信息
